Skip to content

*: cp 67857 #68244

Merged
ti-chi-bot[bot] merged 2 commits into
pingcap:feature/release-8.5-materialized-view-2603from
windtalker:cp_67820
May 9, 2026
Merged

*: cp 67857 #68244
ti-chi-bot[bot] merged 2 commits into
pingcap:feature/release-8.5-materialized-view-2603from
windtalker:cp_67820

Conversation

@windtalker
Copy link
Copy Markdown
Contributor

@windtalker windtalker commented May 9, 2026

What problem does this PR solve?

Issue Number: close #xxx

Problem Summary:

What changed and how does it work?

Check List

Tests

  • Unit test
  • Integration test
  • Manual test (add detailed scripts or steps below)
  • No need to test
    • I checked and no code files have been changed.

Side effects

  • Performance regression: Consumes more CPU
  • Performance regression: Consumes more Memory
  • Breaking backward compatibility

Documentation

  • Affects user behaviors
  • Contains syntax changes
  • Contains variable changes
  • Contains experimental features
  • Changes MySQL compatibility

Release note

Please refer to Release Notes Language Style Guide to write a quality release note.

None

Summary by CodeRabbit

Release Notes

New Features

  • Added tidb_enable_cache_prepare_stmt system variable (enabled by default) to cache prepared statement metadata within a session when the same SQL is prepared multiple times.
  • When enabled, subsequent prepares of identical SQL reuse cached templates, improving performance while maintaining isolation by database and automatic invalidation on schema changes.

Tests

  • Added comprehensive test coverage for prepared statement dedup cache behavior.

guo-shaoge and others added 2 commits May 9, 2026 12:31
@ti-chi-bot ti-chi-bot Bot added release-note-none Denotes a PR that doesn't merit a release note. sig/planner SIG: Planner size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. labels May 9, 2026
@tiprow
Copy link
Copy Markdown

tiprow Bot commented May 9, 2026

Hi @windtalker. Thanks for your PR.

PRs from untrusted users cannot be marked as trusted with /ok-to-test in this repo meaning untrusted PR authors can never trigger tests themselves. Collaborators can still trigger tests on the PR using /test all.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 9, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a session-level prepared-statement deduplication cache that avoids redundant full preparations by caching and rebuilding from template metadata. A new tidb_enable_cache_prepare_stmt system variable controls the feature. The implementation includes helper APIs for parameter marker extraction and plan-cache metadata collection, core dedup logic in the session PrepareStmt method, comprehensive unit tests validating correctness across schema changes and database isolation, and integration test coverage.

Changes

Prepared Statement Deduplication Cache

Layer / File(s) Summary
Cache Entry Types and Structures
pkg/planner/core/plan_cache_utils.go, pkg/sessionctx/variable/session.go, pkg/sessionctx/variable/tidb_vars.go
PrepareStmtCacheEntry struct stores prepared plan-cache statement, resolved fields, and parameter count. SessionVars adds EnableCachePrepareStmt boolean flag and internal prepareStmtDedupCache LRU. TiDBEnableCachePrepareStmt constant and DefEnableCachePrepareStmt = true default defined.
Plan Cache Metadata APIs
pkg/planner/core/plan_cache_utils.go
ExtractAndSortParamMarkers collects and sorts ? parameter markers by offset, assigning order indices and NULL Datum. CollectPlanCacheStmtInfo populates limits, hasSubquery, and tables on a PlanCacheStmt. DBName(), Tbls(), and SetDBNameAndTbls(...) methods manage ownership-safe field access with slice cloning.
System Variable Integration
pkg/sessionctx/variable/session.go, pkg/sessionctx/variable/sysvar.go, pkg/sessionctx/variable/setvar_affect.go
NewSessionVars initializes EnableCachePrepareStmt from DefEnableCachePrepareStmt. PrepareDedupCacheKey builds cache keys from SQL, charset, collation, current DB, and SQL mode. GetPrepareStmtDedupCache and SetPrepareStmtDedupCache lazily manage LRU (bounded by SessionPlanCacheSize). System variable entry in defaultSysVars supports SET/hint-based updates.
Session Prepare Deduplication Logic
pkg/session/session.go
PrepareStmt computes dedup key and checks cache before prepare; on hit, calls rebuildFromPrepareCache to re-parse SQL, extract param markers, run preprocess, clone cached state, and return early with new statement ID. On miss, executes full prepare and caches result. rebuildFromPrepareCache re-parses with session charset/collation, extracts fresh markers, builds aligned ResolveCtx, checks schema version, constructs new PlanCacheStmt from cached immutables plus cloned mutables, collects plan-cache info, and copies DB/table mappings. Also imports maps package for cloning.
Unit Tests
pkg/session/test/common/BUILD.bazel, pkg/session/test/common/prepare_dedup_cache_test.go
Five test functions validate dedup cache: TestPrepareStmtDedupCacheBasic confirms reuse with distinct statement IDs and matching metadata. TestPrepareStmtDedupCacheExecute verifies execution correctness from both full build and cache-hit paths. TestPrepareStmtDedupCacheSchemaChange confirms DDL invalidates cache. TestPrepareStmtDedupCacheIsolatedByDB ensures per-database isolation. TestPrepareStmtDedupCachePrepareExecuteCloseLoop validates repeated prepare-execute-close cycles. Bazel config increased shard_count from 10 to 15.
Integration Tests and Infrastructure
pkg/server/internal/testserverclient/server_client.go, tests/integrationtest/t/sessionctx/setvar.test, tests/integrationtest/r/sessionctx/setvar.result
RunTestInfoschemaClientErrors disables tidb_enable_cache_prepare_stmt and defers reset to ensure controlled caching during error/warning accounting. Integration test covers SET/hint-based variable transitions among 0, 1, off, on, and default values with result validation.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • pingcap/tidb#67857: Directly related; introduces the same PrepareStmtCacheEntry, ExtractAndSortParamMarkers, CollectPlanCacheStmtInfo APIs, session dedup LRU cache, system variable plumbing, and prepare_dedup_cache tests.
  • pingcap/tidb#67820: Directly related; implements the same prepare-stmt dedup/cache feature with identical changes across planner, session, and variable packages.
  • pingcap/tidb#67535: Related; modifies pkg/planner/core/plan_cache_utils.go to add PlanCacheStmt-related helpers (hint-based logic vs. dedup metadata helpers).

Suggested labels

sig/planner, cherry-pick-approved, type/cherry-pick-for-release-8.5, size/XL, ok-to-test

Suggested reviewers

  • qw4990
  • yudongusa
  • guo-shaoge

Poem

🐰 A prepared statement twice prepared,
Now cached and cloned with learned care—
Rebuild from template, fresh ID in hand,
No full reparse across the land!
Schema shifts? Cache bows out, knows when to flee. 🥕

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely unmodified boilerplate template with no actual issue number, problem summary, changes documentation, or test selections filled in. Complete all required sections: add the issue number (close #67815), describe the problem and solution, indicate which tests are included (unit and integration tests are present), and fill in the release note if applicable.
Title check ❓ Inconclusive The title '*: cp 67857' is vague and lacks meaningful context about the actual changes being introduced. Revise the title to be descriptive of the main change, such as '*: support cache prepared statement in plan cache' or similar, to clearly communicate the purpose of this PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.1)

Error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions
The command is terminated due to an error: can't load config: unsupported version of the configuration: "" See https://golangci-lint.run/docs/product/migration-guide for migration instructions


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
pkg/planner/core/plan_cache_utils.go (1)

661-664: ⚡ Quick win

Make CollectPlanCacheStmtInfo idempotent before AST walk.

CollectPlanCacheStmtInfo currently appends into existing stmt.limits/stmt.tables and keeps hasSubquery sticky. If stmt is reused, stale metadata can accumulate and skew later cache-key/stat-related behavior. Reset the derived fields before collecting.

Proposed patch
 func CollectPlanCacheStmtInfo(ctx context.Context, is infoschema.InfoSchema, stmt *PlanCacheStmt, stmtNode ast.StmtNode) {
+	stmt.limits = nil
+	stmt.tables = nil
+	stmt.hasSubquery = false
 	processor := &planCacheStmtProcessor{ctx: ctx, is: is, stmt: stmt}
 	stmtNode.Accept(processor)
 }

As per coding guidelines, code SHOULD remain maintainable for future readers and avoid subtle state-coupling pitfalls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/planner/core/plan_cache_utils.go` around lines 661 - 664,
CollectPlanCacheStmtInfo should clear derived state on the provided
*PlanCacheStmt before walking the AST: reset stmt.limits and stmt.tables to
empty slices (e.g., nil or freshly allocated empty slices) and set
stmt.hasSubquery = false so repeated calls don't accumulate stale data; do this
at the start of CollectPlanCacheStmtInfo just before creating the
planCacheStmtProcessor and calling stmtNode.Accept(processor).
pkg/server/internal/testserverclient/server_client.go (1)

2584-2587: ⚡ Quick win

Consider adding a comment to explain the variable override.

The code correctly disables tidb_enable_cache_prepare_stmt to ensure the test's error/warning counting logic remains stable, but a brief comment would help future maintainers understand why this override is necessary.

📝 Suggested documentation improvement
+	// Disable prepared-statement caching to ensure consistent error/warning counts
+	// in information_schema tables during the test
 	dbt.MustExec("set @@tidb_enable_cache_prepare_stmt = off")
 	defer func() {
 		dbt.MustExec("set @@tidb_enable_cache_prepare_stmt = default")
 	}()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/server/internal/testserverclient/server_client.go` around lines 2584 -
2587, Add a brief inline comment above the dbt.MustExec call that disables
tidb_enable_cache_prepare_stmt explaining that the test disables cached prepared
statements to ensure stable error/warning counting across repeated prepared
executions, and note that the deferred call restores the setting to default;
reference the dbt.MustExec("set @@tidb_enable_cache_prepare_stmt = off") call
and its deferred dbt.MustExec(... = default) to make the intent and need for the
override clear to future maintainers.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/session/test/common/prepare_dedup_cache_test.go`:
- Around line 162-169: The test's DB-isolation check is too weak: after calling
tk.Session().PrepareStmt(sql) for db2 we only assert require.Len(t, fields2, 1)
which won't catch reuse of the db1 cache entry; update the test to verify that
the prepared metadata actually reflects db2 (not db1) by asserting the column
type or behavior. Specifically, after PrepareStmt(sql) for db2 (where id2 is
returned), add an assertion on fields2[0].Column.GetType() (or equivalent type
field) equals the bigint/mysql.TypeLonglong constant from
"github.com/pingcap/tidb/pkg/parser/mysql", or alternatively execute the
prepared statement against db2 data and assert the numeric result matches
db2.tblx.v; keep the existing require.NotEqual(t, id1, id2) and import the mysql
package if you choose the type assertion approach.

---

Nitpick comments:
In `@pkg/planner/core/plan_cache_utils.go`:
- Around line 661-664: CollectPlanCacheStmtInfo should clear derived state on
the provided *PlanCacheStmt before walking the AST: reset stmt.limits and
stmt.tables to empty slices (e.g., nil or freshly allocated empty slices) and
set stmt.hasSubquery = false so repeated calls don't accumulate stale data; do
this at the start of CollectPlanCacheStmtInfo just before creating the
planCacheStmtProcessor and calling stmtNode.Accept(processor).

In `@pkg/server/internal/testserverclient/server_client.go`:
- Around line 2584-2587: Add a brief inline comment above the dbt.MustExec call
that disables tidb_enable_cache_prepare_stmt explaining that the test disables
cached prepared statements to ensure stable error/warning counting across
repeated prepared executions, and note that the deferred call restores the
setting to default; reference the dbt.MustExec("set
@@tidb_enable_cache_prepare_stmt = off") call and its deferred dbt.MustExec(...
= default) to make the intent and need for the override clear to future
maintainers.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0b6683e2-2eae-403b-8f0f-20f9e09e6c35

📥 Commits

Reviewing files that changed from the base of the PR and between 86c5271 and c3b96bb.

📒 Files selected for processing (11)
  • pkg/planner/core/plan_cache_utils.go
  • pkg/server/internal/testserverclient/server_client.go
  • pkg/session/session.go
  • pkg/session/test/common/BUILD.bazel
  • pkg/session/test/common/prepare_dedup_cache_test.go
  • pkg/sessionctx/variable/session.go
  • pkg/sessionctx/variable/setvar_affect.go
  • pkg/sessionctx/variable/sysvar.go
  • pkg/sessionctx/variable/tidb_vars.go
  • tests/integrationtest/r/sessionctx/setvar.result
  • tests/integrationtest/t/sessionctx/setvar.test

Comment on lines +162 to +169
// Prepare in db2 — must NOT hit the db1 cache entry because currentDB differs.
tk.MustExec("use db2")
id2, _, fields2, err := tk.Session().PrepareStmt(sql)
require.NoError(t, err)
require.NotEqual(t, id1, id2)
// fields2 reflects db2.tblx.v which is bigint (not int), so a fresh build must have run.
require.Len(t, fields2, 1)
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Strengthen the DB-isolation assertion — current check doesn't actually verify isolation.

The comment claims "fields2 reflects db2.tblx.v which is bigint (not int), so a fresh build must have run", but the only assertion is require.Len(t, fields2, 1). That length check would pass identically if the dedup cache had (incorrectly) reused the db1 entry — both schemas have a single column. The test will not catch a regression where dedup keys ignore CurrentDB.

Tighten the check by asserting on the column type or by executing the prepared stmt against db2 data and verifying the result.

🔧 Suggested stronger assertion
 	// Prepare in db2 — must NOT hit the db1 cache entry because currentDB differs.
 	tk.MustExec("use db2")
 	id2, _, fields2, err := tk.Session().PrepareStmt(sql)
 	require.NoError(t, err)
 	require.NotEqual(t, id1, id2)
-	// fields2 reflects db2.tblx.v which is bigint (not int), so a fresh build must have run.
 	require.Len(t, fields2, 1)
+	// fields2 must reflect db2.tblx.v (bigint), not the cached db1 schema (int);
+	// otherwise dedup is incorrectly ignoring CurrentDB.
+	require.Equal(t, mysql.TypeLonglong, fields2[0].Column.FieldType.GetType(),
+		"db2.tblx.v should be BIGINT; a stale db1 cache hit would return INT")
+	require.Equal(t, mysql.TypeLong, fields1[0].Column.FieldType.GetType(),
+		"sanity: db1.tblx.v is INT")

You'll need to import "github.com/pingcap/tidb/pkg/parser/mysql".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/session/test/common/prepare_dedup_cache_test.go` around lines 162 - 169,
The test's DB-isolation check is too weak: after calling
tk.Session().PrepareStmt(sql) for db2 we only assert require.Len(t, fields2, 1)
which won't catch reuse of the db1 cache entry; update the test to verify that
the prepared metadata actually reflects db2 (not db1) by asserting the column
type or behavior. Specifically, after PrepareStmt(sql) for db2 (where id2 is
returned), add an assertion on fields2[0].Column.GetType() (or equivalent type
field) equals the bigint/mysql.TypeLonglong constant from
"github.com/pingcap/tidb/pkg/parser/mysql", or alternatively execute the
prepared statement against db2 data and assert the numeric result matches
db2.tblx.v; keep the existing require.NotEqual(t, id1, id2) and import the mysql
package if you choose the type assertion approach.

@ti-chi-bot ti-chi-bot Bot added the needs-1-more-lgtm Indicates a PR needs 1 more LGTM. label May 9, 2026
Copy link
Copy Markdown
Contributor

@xzhangxian1008 xzhangxian1008 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented May 9, 2026

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: guo-shaoge, xzhangxian1008
Once this PR has been reviewed and has the lgtm label, please assign time-and-fate, yudongusa for approval. For more information see the Code Review Process.
Please ensure that each of them provides their approval before proceeding.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@ti-chi-bot ti-chi-bot Bot added lgtm and removed needs-1-more-lgtm Indicates a PR needs 1 more LGTM. labels May 9, 2026
@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented May 9, 2026

[LGTM Timeline notifier]

Timeline:

  • 2026-05-09 04:45:36.820770965 +0000 UTC m=+502209.694120937: ☑️ agreed by guo-shaoge.
  • 2026-05-09 06:16:57.880193094 +0000 UTC m=+507690.753543066: ☑️ agreed by xzhangxian1008.

@tiprow
Copy link
Copy Markdown

tiprow Bot commented May 9, 2026

@windtalker: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
fast_test_tiprow_for_release c3b96bb link true /test fast_test_tiprow_for_release

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@timzxz
Copy link
Copy Markdown

timzxz commented May 9, 2026

/override fast_test_tiprow_for_release idc-jenkins-ci-tidb/check_dev idc-jenkins-ci-tidb/check_dev_2 idc-jenkins-ci-tidb/mysql-test idc-jenkins-ci-tidb/unit-test

@ti-chi-bot
Copy link
Copy Markdown

ti-chi-bot Bot commented May 9, 2026

@timzxz: Overrode contexts on behalf of timzxz: fast_test_tiprow_for_release, idc-jenkins-ci-tidb/check_dev, idc-jenkins-ci-tidb/check_dev_2, idc-jenkins-ci-tidb/mysql-test, idc-jenkins-ci-tidb/unit-test

Details

In response to this:

/override fast_test_tiprow_for_release idc-jenkins-ci-tidb/check_dev idc-jenkins-ci-tidb/check_dev_2 idc-jenkins-ci-tidb/mysql-test idc-jenkins-ci-tidb/unit-test

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@ti-chi-bot ti-chi-bot Bot merged commit 9984795 into pingcap:feature/release-8.5-materialized-view-2603 May 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved lgtm release-note-none Denotes a PR that doesn't merit a release note. sig/planner SIG: Planner size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants